//
// Created by yanhai on 2021/3/29.
//

#include <iostream>
#include "timewheel.h"

uint64_t gettime()
{
    uint64_t t;
#if !defined(__APPLE__) || defined(AVAILABLE_MAC_OS_X_VERSION_10_12_AND_LATER)
    struct timespec ti;
    clock_gettime(CLOCK_MONOTONIC, &ti);
    t = (uint64_t) ti.tv_sec * 100;
    t += ti.tv_nsec / 10000000;
#else
    struct timeval tv;
    gettimeofday(&tv, NULL);
    t = (uint64_t)tv.tv_sec * 100;
    t += tv.tv_usec / 10000;
#endif
    return t;
}

timer::timer()
{
    current_time = 0;
    current_point = gettime();
}

bool timer::add_timer(uint32_t interval, std::function<void(void *)> func, void *arg)
{
    timer_node node{};
    node.callback = std::move(func);
    node.cancel = 0;
    node.arg = arg;
    node.expire = current_time + interval;

    // 立即执行
    if (interval == 0) {
        node.callback(node.arg);
        return true;
    }

    // 添加到定时器
    lock.lock();
    add_node(node);
    lock.unlock();

    return true;
}

void timer::expire_timer()
{
    uint64_t cp = gettime();
    if (cp != current_point) {
        auto diff = (uint32_t) (cp - current_point);
        current_point = cp;
        for (decltype(diff) i = 0; i < diff; i++) {
//            printf("current_time=%u\n", current_time);
            timer_update();
        }
    }
}

// 将当前list中的节点重新加入到定时器中，以做到重新映射的目的
void timer::move_list(int level, int idx)
{
    auto &list = t[level][idx];
    for (auto &node: list) {
        add_node(node);
    }
    list.clear();
}

void timer::timer_shift()
{
    int mask = TIME_NEAR;
    uint32_t ct = ++current_time;
    if (ct == 0) {
        move_list(3, 0);
    } else {
        // ct / 256
        uint32_t time = ct >> TIME_NEAR_SHIFT;
        int i = 0;
        // ct % 256 == 0
        while ((ct & (mask - 1)) == 0) {
            auto idx = time & TIME_LEVEL_MASK;
            if (idx != 0) {
                move_list(i, idx);
                break;
            }
            mask <<= TIME_LEVEL_SHIFT;
            time >>= TIME_LEVEL_SHIFT;
            ++i;
        }
    }
}

void timer::timer_update()
{
    lock.lock();
    timer_execute();
    timer_shift();
    timer_execute();
    lock.unlock();
}

/**
 * \brief 执行这个list中的所有任务
 * \param list
 */
void timer::dispatch_list(const std::list<timer_node> &list)
{
    for (auto &n: list) {
        if (!n.cancel) {
            n.callback(n.arg);
        }
    }
}

void timer::timer_execute()
{
    auto idx = current_time & TIME_NEAR_MASK;

    // 有可能在执行的时候，又向这个idx位置的链表添加节点了，因此我们用while
    while (!near[idx].empty()) {
//        auto list = near[idx]; // 直接把整个链表拷贝过来
//        near[idx].clear();
        //decltype(near[idx]) list; // list是引用类型...
        std::list<timer_node> list;
        list.swap(near[idx]);   // 快速得到near[idx]，并把它清空

        lock.unlock(); // 解锁，然后依次执行list中的任务
        dispatch_list(list);
        lock.lock();
    }
}

void timer::add_node(const timer_node &node)
{
    bool is_near = false;
    uint32_t level, idx;
    std::list<timer_node> *list;
    auto expire = node.expire;
    uint32_t msec = expire - current_time;

    if (msec < TIME_NEAR) { //[0, 0x100)
        is_near = true;
        idx = expire & TIME_NEAR_MASK;
    } else if (msec < (1 << (TIME_NEAR_SHIFT + TIME_LEVEL_SHIFT))) {//[0x100, 0x4000)
        level = 0;
        idx = (expire >> TIME_NEAR_SHIFT) & TIME_LEVEL_MASK;
    } else if (msec < (1 << (TIME_NEAR_SHIFT + 2 * TIME_LEVEL_SHIFT))) {//[0x4000, 0x100000)
        level = 1;
        idx = ((expire >> (TIME_NEAR_SHIFT + TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK);
    } else if (msec < (1 << (TIME_NEAR_SHIFT + 3 * TIME_LEVEL_SHIFT))) {//[0x100000, 0x4000000)
        level = 2;
        idx = ((expire >> (TIME_NEAR_SHIFT + 2 * TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK);
    } else {//[0x4000000, 0xffffffff]
        level = 3;
        idx = ((expire >> (TIME_NEAR_SHIFT + 3 * TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK);
    }

    if (is_near) {
        list = &near[idx];
//        std::cout << "append near[" << idx << "]" << std::endl;
    } else {
        list = &t[level][idx];
//        std::cout << "append t[" << level << "][" << idx << "]" << std::endl;
    }

    list->push_back(node);
}
